---
title: .family
---

import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
import { Link } from "../../../../../../src/components/Link";

:::caution
The content of this page may be outdated.  
It will be updated in the future, but for now you may want to refer to the content
in the top of the sidebar instead (introduction/essentials/case-studies/...)
:::

Before reading this, consider reading about <Link documentID="concepts2/providers"/> and <Link documentID="concepts/reading"/>.
In this part, we will talk in detail about the `.family` provider modifier.

The `.family` modifier has one purpose: Getting a unique provider based on external parameters.

Some common use-cases for `family` would be:

- Combining [FutureProvider] with `.family` to fetch a `Message` from its ID
- Passing the current `Locale` to a provider, so that we can handle translations

## Usage

The way families works is by adding an extra parameter to the provider.
This parameter can then be freely used in our provider to create some state.

For example, we can combine `family` with [FutureProvider] to fetch
a `Message` from its ID:

```dart
final messagesFamily = FutureProvider.family<Message, String>((ref, id) async {
  return dio.get('http://my_api.dev/messages/$id');
});
```

When using our `messagesFamily` provider, the syntax is slightly different.  
The usual syntax will not work anymore:

```dart
Widget build(BuildContext context, WidgetRef ref) {
  // Error – messagesFamily is not a provider
  final response = ref.watch(messagesFamily);
}
```

Instead, we need to pass a parameter to `messagesFamily`:

```dart
Widget build(BuildContext context, WidgetRef ref) {
  final response = ref.watch(messagesFamily('id'));
}
```

:::info
It is possible to use a family with different parameters simultaneously.  
For example, we could use a `titleFamily` to read both the French and English
translations at the same time:

```dart
@override
Widget build(BuildContext context, WidgetRef ref) {
  final frenchTitle = ref.watch(titleFamily(const Locale('fr')));
  final englishTitle = ref.watch(titleFamily(const Locale('en')));

  return Text('fr: $frenchTitle en: $englishTitle');
}
```

:::

## Parameter restrictions

For families to work correctly, it is critical for the parameter passed to
a provider to have a consistent `hashCode` and `==`.

Ideally, the parameter should either be a primitive (bool/int/double/String),
a constant (providers), or an immutable object that overrides `==` and `hashCode`.

### _PREFER_ using `autoDispose` when the parameter is not constant:

You may want to use families to pass the input of a search field to your provider.
But that value can change often and never be reused.  
This could cause memory leaks as, by default, a provider is never destroyed even
if no longer used.

Using both `.family` and `.autoDispose` fixes that memory leak:

```dart
final characters = FutureProvider.autoDispose.family<List<Character>, String>((ref, filter) async {
  return fetchCharacters(filter: filter);
});
```

## Passing multiple parameters to a family

Families have no built-in support for passing multiple values to a provider.

On the other hand, that value could be _anything_ (as long as it matches with
the restrictions mentioned previously).

This includes:

- A tuple from [tuple](http://pub.dev/packages/tuple)
- Objects generated with [Freezed] or [built_value](https://pub.dev/packages/built_value)
- Objects using [equatable](https://pub.dev/packages/equatable)

Here's an example of using [Freezed] or [equatable] for multiple parameters:

<Tabs
  groupId="family"
  defaultValue="freezed"
  values={[
    { label: 'Freezed', value: 'freezed', },
    { label: 'Equatable', value: 'equatable', },
  ]}
>

<TabItem value="freezed">

```dart
@freezed
abstract class MyParameter with _$MyParameter {
  factory MyParameter({
    required int userId,
    required Locale locale,
  }) = _MyParameter;
}

final exampleProvider = Provider.autoDispose.family<Something, MyParameter>((ref, myParameter) {
  print(myParameter.userId);
  print(myParameter.locale);
  // Do something with userId/locale
});

@override
Widget build(BuildContext context, WidgetRef ref) {
  int userId; // Read the user ID from somewhere
  final locale = Localizations.localeOf(context);

  final something = ref.watch(
    exampleProvider(MyParameter(userId: userId, locale: locale)),
  );

  ...
}
```

</TabItem>
<TabItem value="equatable">

```dart
class MyParameter extends Equatable  {
  MyParameter({
    required this.userId,
    required this.locale,
  });

  final int userId;
  final Locale locale;

  @override
  List<Object> get props => [userId, locale];
}

final exampleProvider = Provider.family<Something, MyParameter>((ref, myParameter) {
  print(myParameter.userId);
  print(myParameter.locale);
  // Do something with userId/locale
});

@override
Widget build(BuildContext context, WidgetRef ref) {
  int userId; // Read the user ID from somewhere
  final locale = Localizations.localeOf(context);

  final something = ref.watch(
    exampleProvider(MyParameter(userId: userId, locale: locale)),
  );

  ...
}
```

</TabItem>
</Tabs>

[freezed]: https://pub.dev/packages/freezed
[equatable]: https://pub.dev/packages/equatable
[futureprovider]: https://pub.dev/documentation/hooks_riverpod/latest/hooks_riverpod/FutureProvider-class.html
